分类
联系方式
  1. 新浪微博
  2. E-mail

Flutter 渲染流程

整体流程与渲染树

<a id='drawio-img-href-876882031' href='/mediawiki/index.php?title=File:Flutter%E6%95%B4%E4%BD%93%E6%B5%81%E7%A8%8B%E4%B8%8E%E6%B8%B2%E6%9F%93%E6%A0%91.drawio.png'><img id='drawio-img-876882031' src='/mediawiki/images/5/5c/Flutter%E6%95%B4%E4%BD%93%E6%B5%81%E7%A8%8B%E4%B8%8E%E6%B8%B2%E6%9F%93%E6%A0%91.drawio.png' title='drawio: Flutter整体流程与渲染树' alt='drawio: Flutter整体流程与渲染树' style='height: auto; width: 100%; max-width: 946px;' /></img></a>

runApp 首帧绘制

分析的入口在 Flutter main 函数调用 runApp 方法。传入根组件,具体实现:

void runApp(Widget app) {
  WidgetsFlutterBinding.ensureInitialized()
    ..scheduleAttachRootWidget(app)
    ..scheduleWarmUpFrame();
}

其中:

  • scheduleAttachRootWidget:向 EventQueue 调度一个事件,将根组件挂在到组件树上
  • scheduleWarmUpFrame:快速触发一帧调度,实现快速上屏
    • handleBeginFrame、handleDrawFrame 上屏流程
    • scheduleFrame 开启帧调度

开启帧调度

上一步调用 binding.dart scheduleFrame 出发帧调度开启,具体流程是:

  • window.scheduleFrame
  • platformDispatcher.scheduleFrame
  • "PlatformConfiguration_scheduleFrame"
  • RuntimeController::ScheduleFrame
  • Engine::ScheduleFrame
  • Animator::RequestFrame
  • self->AwaitVSync

等待系统下一帧 VSYNC 信号到来。

VSYNC 帧调度

当下一帧 VSYNC 信号到来,在 Animator::AwaitVSync 的回调中,判断 Layer 是否需要复用,如果不复用,则出发一帧绘制流程:

void Animator::AwaitVSync() {
  waiter_->AsyncWaitForVsync(
      [self = weak_factory_.GetWeakPtr()](
          std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder) {
        if (self) {
          if (self->CanReuseLastLayerTree()) {
            self->DrawLastLayerTree(std::move(frame_timings_recorder));
          } else {
            self->BeginFrame(std::move(frame_timings_recorder));
          }
        }
      });

  delegate_.OnAnimatorNotifyIdle(dart_frame_deadline_);
}

BeginFrame 透传

Animator::BeginFrame 的核心代码调用有:

  • delegate_.OnAnimatorBeginFrame(frame_target_time, frame_number);
  • self->delegate_.OnAnimatorNotifyIdle(Dart_TimelineGetMicros() + 100000);
  • 其中的 delegate_ 是 shell

shell:

  • OnAnimatorBeginFrame:engine_->BeginFrame
  • OnAnimatorNotifyIdle:engine_->NotifyIdle

engine_->BeginFrame 后续的分发流程是:

  • engine.cc: BeginFrame
  • runtime_controller.cc: BeginFrame
  • window.cc: BeginFrame
  • PlatformConfiguration::BeginFrame

PlatformConfiguration::BeginFrame 中做了几件事情,核心方法如下:

// 调 dart _beginFrame
tonic::LogIfError(
    tonic::DartInvoke(begin_frame_.Get(), {
                                              Dart_NewInteger(microseconds),
                                              Dart_NewInteger(frame_number),
                                          }));
// 刷一遍微任务队列
UIDartState::Current()->FlushMicrotasksNow();

// 掉 dart _drawFrame
tonic::LogIfError(tonic::DartInvokeVoid(draw_frame_.Get()));

_beginFrame 调用

DartInvoke 调用的是入口方法,即 entry-point,来到 hooks.dart 下对应方法:

@pragma('vm:entry-point')
// ignore: unused_element
void _beginFrame(int microseconds, int frameNumber) {
  PlatformDispatcher.instance._beginFrame(microseconds);
  PlatformDispatcher.instance._updateFrameData(frameNumber);
}

lib/ui/window/platform_configuration.cc 的 _onBeginFrame:

// Called from the engine, via hooks.dart
void _beginFrame(int microseconds) {
  _invoke1<Duration>(
    onBeginFrame,
    _onBeginFrameZone,
    Duration(microseconds: microseconds),
  );
}

来到 binding.dart 的 _handleBeginFrame:

void _handleBeginFrame(Duration rawTimeStamp) {
  ……
  handleBeginFrame(rawTimeStamp);
}

handleBeginFrame 是核心方法,核心操作是 _invokeFrameCallback 进行回调调用。

_invokeFrameCallback 方法比较关键。

_drawFrame

lib/ui/window/platform_configuration.cc 的 _drawFrame:

@pragma('vm:entry-point')
// ignore: unused_element
void _drawFrame() {
  PlatformDispatcher.instance._drawFrame();
}

调用到 lib/src/scheduler/binding.dart 的 _handleDrawFrame:

void _handleDrawFrame() {
  if (_rescheduleAfterWarmUpFrame) {
    _rescheduleAfterWarmUpFrame = false;
    addPostFrameCallback((Duration timeStamp) {
      _hasScheduledFrame = false;
      scheduleFrame();
    });
    return;
  }
  handleDrawFrame();
}

其中:

  • 先同步调 handleDrawFrame() 绘制当前帧
  • 异步 addPostFrameCallback 注册下一帧调度 scheduleFrame()

handleDrawFrame 中遍历执行几个回调:

  • persistentCallbacks
  • postFrameCallbacks

RendererBinding.drawFrame

RendererBinding 的 initInstances 中向 SchedulerBinding 设置了一个 PersistentFrameCallback:

addPersistentFrameCallback(_handlePersistentFrameCallback);

这个回调在 SchedulerBinding 的 handleDrawFrame 方法中调用。会调到 drawFrame 方法。

@protected
void drawFrame() {
  assert(renderView != null);
  pipelineOwner.flushLayout();
  pipelineOwner.flushCompositingBits();
  pipelineOwner.flushPaint();
  if (sendFramesToEngine) {
    renderView.compositeFrame(); // this sends the bits to the GPU
    pipelineOwner.flushSemantics(); // this also sends the semantics to the OS.
    _firstFrameSent = true;
  }
}

这里需要注意的是,WidgetBinding mixin 了 drawFrame 方法,因此会首先运行 WidgetBinding.drawFrame 再运行上面这个。

WidgetBinding.drawFrame 是 Flutter Framework 中最核心的点之一,三棵树构建就在这里触发。主要包含以下操作:

  • 对 dirty 元素进行重新 build:buildOwner!.buildScope
  • 执行 RendererBinding.drawFrame:super.drawFrame(); 这会生成传给引擎的 Layer
    • flushLayout:计算渲染对象的大小和位置
    • CompositingBits:更新具有脏合成位的渲染对象
    • flushPaint:将绘制命令记录到 Layer
    • Compositing:将 CompositingBits 发送给 GPU,合成帧
    • 更新语意树